package com.yhq.basic.algorithm.array;

import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * @author hqyin
 * @date 2023/8/17 2:48 下午
 */
public class MyArrayList<T> {

    private static final int DEFAULT_CAPACITY = 10;
    private static final Object[] EMPTY_ELEMENT_DATA = {};
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private int size;
    private transient Object[] data;

    public MyArrayList() {
        data = EMPTY_ELEMENT_DATA;
    }

    Stream<T> stream() {
        return StreamSupport.stream(spliterator(),false);
    }

     Spliterator<T> spliterator() {
        return Spliterators.spliterator(data,0);
    }

    public void add(T obj) {
        ensureCapacity(size+1);
        data[size++]= obj;
    }

    private void ensureCapacity(int minCapacity) {
        int mewCapacity = calculateCapacity(data,minCapacity);
        if(mewCapacity-data.length>0) {
            grow(mewCapacity);
        }
    }

    private int calculateCapacity(Object[] data, int minCapacity) {
        if(data == EMPTY_ELEMENT_DATA) {
            return Math.max(minCapacity,DEFAULT_CAPACITY);
        }
        return minCapacity;
    }

    private void grow(int minCapacity) {
        int oldCapacity = data.length;
        int newCapacity = oldCapacity + oldCapacity >> 1;
        if(newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }

        if(newCapacity - MAX_ARRAY_SIZE >0) {
            newCapacity = Integer.MAX_VALUE;
        }

        Object[] copy = new Object[newCapacity];
        System.arraycopy(data,0,copy,0,Math.min(data.length,newCapacity));
        data = copy;
    }

    public T get(int index) {
        rangeCheck(index);
        return (T) data[index];
    }

    private void rangeCheck(int index) {
        if(index >= size) {
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
        }
    }

    public int size() {
        return this.size;
    }

    public T remove(int index) {
        rangeCheck(index);
        T oldValue = (T) data[index];
        int numMoved = size-index-1;
        if(numMoved>0) {
            System.arraycopy(data,index+1,data,index,numMoved);
        }
        data[size--] = null;
        return oldValue;
    }

    public T set(int index, T obj) {
        rangeCheck(index);
        T old = (T) data[index];
        data[index] = obj;
        return old;
    }

    public void clear() {
        for (int i = 0; i < this.size; i++) {
            data[i] = null;
        }
        this.size=0;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("MyArrayList[");
        for (int i = 0; i < size; i++) {
            if(i== 0) {
                sb.append(data[i]);
            } else {
                sb.append(",");
                sb.append(data[i]);
            }
        }
        sb.append("]");

        return sb.toString();
    }
}
